package com.ssy.lingxi.settle.accounts.serviceimpl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.google.common.collect.Lists;
import com.querydsl.core.Tuple;
import com.querydsl.core.group.GroupBy;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.basic.PurchaseContractPayTypeEnum;
import com.ssy.lingxi.common.constant.settle.ReceiptInvoiceOrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementOrderTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.component.redis.service.IRedisUtils;
import com.ssy.lingxi.dto.model.constant.ApplyAmountRowBillTypeEnum;
import com.ssy.lingxi.dto.model.constant.SettlementHelpEnum;
import com.ssy.lingxi.dto.request.LogOtherStateVO;
import com.ssy.lingxi.dto.request.MemberReceiptInvoiceBaseVO;
import com.ssy.lingxi.dto.response.CorporateAccountConfigResponse;
import com.ssy.lingxi.order.api.model.vo.request.OrderFeignIdsVO;
import com.ssy.lingxi.order.api.model.vo.request.OrderUpdateDeliveriesFeignVO;
import com.ssy.lingxi.order.api.model.vo.response.OrderBuyerSettleReconciliationFeignVO;
import com.ssy.lingxi.settle.accounts.common.SettleAccountsCommon;
import com.ssy.lingxi.settle.accounts.entity.*;
import com.ssy.lingxi.settle.accounts.model.bo.ApplyAmountDetailBO;
import com.ssy.lingxi.settle.accounts.model.bo.InvoiceProveBO;
import com.ssy.lingxi.settle.accounts.model.constant.*;
import com.ssy.lingxi.settle.accounts.model.vo.request.*;
import com.ssy.lingxi.settle.accounts.model.vo.response.*;
import com.ssy.lingxi.settle.accounts.repository.*;
import com.ssy.lingxi.settle.accounts.service.*;
import com.ssy.lingxi.settle.accounts.utils.NumberUtil;
import com.ssy.lingxi.settle.accounts.utils.OrderDateTimeUtil;
import com.ssy.lingxi.settle.accounts.utils.SerialNumberUtils;
import com.ssy.lingxi.workflow.api.model.contant.ProcessEnum;
import com.ssy.lingxi.workflow.api.model.vo.request.ExternalProcessQueryVO;
import com.ssy.lingxi.workflow.api.model.vo.request.TaskExecuteVO;
import com.ssy.lingxi.workflow.api.model.vo.response.ComplexTaskCompleteVO;
import com.ssy.lingxi.workflow.api.model.vo.response.SimpleProcessDefVO;
import com.ssy.lingxi.workflow.api.model.vo.response.SimpleTaskVO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务对账单接口实现
 * @author Saske
 * @date 2021/12/7
 * @version 2.0.0
 */
@Service
public class BusinessReconciliationServiceImpl implements IBusinessReconciliationService {

    @Resource
    private IRedisUtils redisUtils;

    @Resource
    private IStatementCirculationRecordService recordService;

    @Resource
    private BusinessReconciliationRepository repository;

    @Resource
    private BusinessReconciliationRowRepository detailRepository;

    @Resource
    private BusinessReconciliationInvoiceRecordRepository invoiceRecordRepository;

    @Resource
    private ApplyAmountRepository applyAmountRepository;

    @Resource
    private JPAQueryFactory jpaQueryFactory;

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private CorporateAccountConfigService configService;

    @Resource
    private SettlementProductRepository productRepository;

    @Resource
    private SettlementOrderRepository orderRepository;

    @Resource
    private IMemberReceiptInvoiceService invoiceService;

    @Resource
    private IReconciliationFileService fileService;

    @Resource
    private IFeignService feignService;

    /**
     * 待对账列表
     * @author Saske
     * @param sysUser saveVo
     * @return 待对账分页列表
     **/
    @Override
    public Wrapper<PageData<ToBeReconciliationVO>> toBeReconciliationList(UserLoginCacheDTO sysUser, ToBeReconciliationListVO queryParameter) {

        QSettlementOrderDO qOrder = QSettlementOrderDO.settlementOrderDO;
        QSettlementProductDO qProduct = QSettlementProductDO.settlementProductDO;
        //由于QueryDsl的关联查询中，无法对主表进行分页，所以要分开查询
        // step 1: 构造总数查询，所有查询条件的表都必须关联定义
        JPAQuery<Tuple> countQuery = jpaQueryFactory.select(qOrder.id, qProduct.expectPayTime.max()).from(qOrder)
                .leftJoin(qProduct).on(qOrder.id.eq(qProduct.order.id))
                .where(qProduct.reconciliationQuantity.gt(BigDecimal.ZERO));
        // step 2: 查询所有条件
        JPAQuery<?> allQuery = jpaQueryFactory.from(qOrder).distinct()
                .leftJoin(qProduct).on(qOrder.id.eq(qProduct.order.id))
                .where(qProduct.reconciliationQuantity.gt(BigDecimal.ZERO));

        // step 3: 固定 查询条件
        // 供应商会员,供应商会员角色
        countQuery.where(qOrder.vendorMemberId.eq(sysUser.getMemberId()).and(qOrder.vendorRoleId.eq(sysUser.getMemberRoleId())));
        allQuery.where(qOrder.vendorMemberId.eq(sysUser.getMemberId()).and(qOrder.vendorRoleId.eq(sysUser.getMemberRoleId())));
        // 获取前一天符合条件的数据
        countQuery.where(qOrder.receiveTime.before(LocalDateTime.parse(OrderDateTimeUtil.getCurrentDate().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        allQuery.where(qOrder.receiveTime.before(LocalDateTime.parse(OrderDateTimeUtil.getCurrentDate().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));

        // step 4: 动态的查询条件
        // 订单单号
        if (StringUtils.hasLength(queryParameter.getBillNo())){
            countQuery.where(qOrder.billNo.like("%" + queryParameter.getBillNo().trim() + "%"));
            allQuery.where(qOrder.billNo.like("%" + queryParameter.getBillNo().trim() + "%"));
        }

        // 发货单号
        if(StringUtils.hasLength(queryParameter.getDeliveryNo())) {
            countQuery.where(qOrder.deliveryNo.like("%" + queryParameter.getDeliveryNo().trim() + "%"));
            allQuery.where(qOrder.deliveryNo.like("%" + queryParameter.getDeliveryNo().trim() + "%"));
        }

        // 收货单号
        if(StringUtils.hasLength(queryParameter.getReceiveNo())) {
            countQuery.where(qOrder.receiveNo.like("%" + queryParameter.getReceiveNo().trim() + "%"));
            allQuery.where(qOrder.receiveNo.like("%" + queryParameter.getReceiveNo().trim() + "%"));
        }

        // 付款方
        if(StringUtils.hasLength(queryParameter.getPayer())) {
            countQuery.where(qOrder.payer.like("%" + queryParameter.getPayer().trim() + "%"));
            allQuery.where(qOrder.payer.like("%" + queryParameter.getPayer().trim() + "%"));
        }

        // 收款方
        if(StringUtils.hasLength(queryParameter.getPayee())) {
            countQuery.where(qOrder.payee.like("%" + queryParameter.getPayee().trim() + "%"));
            allQuery.where(qOrder.payee.like("%" + queryParameter.getPayee().trim() + "%"));
        }

        // 结算方式
        if(NumberUtil.notNullOrZero(queryParameter.getPayWay())) {
            countQuery.where(qProduct.payWay.eq(queryParameter.getPayWay()));
            allQuery.where(qProduct.payWay.eq(queryParameter.getPayWay()));
        }

        // 税率
        if(Objects.nonNull(queryParameter.getTaxRate())) {
            countQuery.where(qProduct.taxRate.eq(queryParameter.getTaxRate()));
            allQuery.where(qProduct.taxRate.eq(queryParameter.getTaxRate()));
        }

        // 预计结算日期起始时间
        if (StringUtils.hasLength(queryParameter.getExpectPayTimeStart())) {
            countQuery.where(qProduct.expectPayTime.after(LocalDateTime.parse(queryParameter.getExpectPayTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            allQuery.where(qProduct.expectPayTime.after(LocalDateTime.parse(queryParameter.getExpectPayTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        // 预计结算日期结束时间
        if (StringUtils.hasLength(queryParameter.getExpectPayTimeEnd())) {
            countQuery.where(qProduct.expectPayTime.before(LocalDateTime.parse(queryParameter.getExpectPayTimeEnd().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            allQuery.where(qProduct.expectPayTime.before(LocalDateTime.parse(queryParameter.getExpectPayTimeEnd().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        // 过滤已选择的商品明细id
        if(!CollectionUtils.isEmpty(queryParameter.getIds())){
            countQuery.where(qProduct.id.notIn(queryParameter.getIds()));
            allQuery.where(qProduct.id.notIn(queryParameter.getIds()));
        }

        // 默认预计结算日期排序
        countQuery.groupBy(qOrder.id);
        countQuery.limit(queryParameter.getPageSize()).offset(queryParameter.getCurrentOffset());
        long totalCount = countQuery.fetchCount();

        // step5: 获取排序得订单id
        List<Long> orderIds = countQuery.fetch().stream().map( map -> map.get(qOrder.id)).distinct().collect(Collectors.toList());
        allQuery.where(qOrder.id.in(orderIds));

        Map<Long, ToBeReconciliationVO> transform = allQuery.transform(GroupBy.groupBy(qOrder.id)
                .as(Projections.constructor(ToBeReconciliationVO.class,
                        qOrder.id,
                        qOrder.billId,
                        qOrder.billNo,
                        qOrder.payer,
                        qOrder.buyerMemberId,
                        qOrder.buyerRoleId,
                        qOrder.deliveryBatch,
                        qOrder.deliveryNo,
                        qOrder.deliveryTime,
                        qOrder.receiveNo,
                        qOrder.receiveTime,
                        qOrder.orderStatus,
                        qOrder.orderType,
                        qOrder.billType,
                        qOrder.billDate,
                        qOrder.billAmount,
                        qOrder.billAbstract,
                        qOrder.sourceContractId,
                        GroupBy.list(Projections.constructor(ToBeReconciliationRowVO.class,
                                qProduct.id,
                                qProduct.payWay,
                                qProduct.payMonth,
                                qProduct.payDate,
                                qProduct.expectPayTime,
                                qProduct.productNo,
                                qProduct.name,
                                qProduct.spec,
                                qProduct.category,
                                qProduct.brand,
                                qProduct.unit,
                                qProduct.taxRate,
                                qProduct.price,
                                qProduct.receiveQuantity,
                                qProduct.reconciledQuantity,
                                qProduct.reconciledMoney,
                                qProduct.reconciliationQuantity,
                                qOrder.billType)))));

        List<ToBeReconciliationVO> queryResult = new ArrayList<>(transform.values());

        return Wrapper.success(new PageData<>(totalCount,queryResult));
    }

    /**
     * 生成对账单数据
     * @author Saske
     * @param sysUser orderVO
     * @return 待新增对账单
     **/
    @Override
    public Wrapper<GenerateReconciliationVO> generateReconciliation(UserLoginCacheDTO sysUser, SettlementGenerateVO orders) {

        GenerateReconciliationVO dataVO = new GenerateReconciliationVO();

        dataVO.setStatus(ReconciliationStatusEnum.TO_SUBMIT.getMessage());
        dataVO.setOutStatus(ReconciliationOuterStatusEnum.TO_SUBMIT.getMessage());
        dataVO.setReconciliationType(ReconciliationTypeEnum.MATERIAL.getMessage());
        dataVO.setPayee(sysUser.getCompany());
        dataVO.setLaunchReconciliation(sysUser.getCompany());

        //判断选择的订单是否同一个付款方
        Set<String> payerSet = orders.getOrderVOS().stream().map(SettlementOrderVO::getPayer).collect(Collectors.toSet());
        //用于判断所有的商品明细是否税率相同
        Set<BigDecimal> taxSet = new HashSet<>();
        List<GenerateReconciliationRowVO> detailVOS = new ArrayList<>();
        for (SettlementOrderVO settlementOrderVO : orders.getOrderVOS()){
            dataVO.setPayer(settlementOrderVO.getPayer());
            for (SettlementProductVO productVO : settlementOrderVO.getProducts()){
                GenerateReconciliationRowVO detailVO = new GenerateReconciliationRowVO();
                // 单据id
                detailVO.setOrderId(settlementOrderVO.getBillId());
                // 单据编码
                detailVO.setOrderNo(settlementOrderVO.getBillNo());
                // 单据状态
                detailVO.setOrderStatus(settlementOrderVO.getOrderStatus());
                // 订单单据类型
                detailVO.setOrderType(settlementOrderVO.getOrderType());
                // 订单单据类型枚举
                detailVO.setOrderTypeName(ApplyAmountTypeEnum.getNameByCode(settlementOrderVO.getOrderType()));
                // 单据类型
                detailVO.setBillType(settlementOrderVO.getBillType());
                detailVO.setBillTypeName(ApplyAmountRowBillTypeEnum.getNameByCode(settlementOrderVO.getBillType()));
                // 单据日期
                detailVO.setBillDate(settlementOrderVO.getBillDate());
                // 单据金额
                detailVO.setBillAmount(settlementOrderVO.getBillAmount());
                // 单据摘要
                detailVO.setBillAbstract(settlementOrderVO.getBillAbstract());
                // 单据来源合同id
                detailVO.setSourceContractId(settlementOrderVO.getSourceContractId());
                // 物料id
                detailVO.setProductId(productVO.getProductId());
                // 预计结算日期
                detailVO.setExpectPayTime(productVO.getExpectPayTime());
                // 发货批次
                detailVO.setDeliveryBatch(settlementOrderVO.getDeliveryBatch());
                // 发货单号
                detailVO.setDeliveryNo(settlementOrderVO.getDeliveryNo());
                // 收货单号
                detailVO.setReceiveNo(settlementOrderVO.getReceiveNo());
                // 物料编码
                detailVO.setProductNo(productVO.getProductNo());
                // 物料名称
                detailVO.setProductName(productVO.getProductName());
                // 商品规格
                detailVO.setSpec(productVO.getSpec());
                // 商品品类
                detailVO.setCategory(productVO.getCategory());
                // 商品品牌
                detailVO.setBrand(productVO.getBrand());
                // 单位
                detailVO.setUnit(productVO.getUnit());
                // 税率（百分比的分子部分）
                detailVO.setTaxRate(productVO.getTaxRate());
                // 单价（含税）
                detailVO.setPrice(productVO.getPrice());
                // 单价（含税）
                detailVO.setReconciliationQuantity(productVO.getReconciliationQuantity());
                // 待对账数量
                detailVO.setReconciliationMoneyAmount(productVO.getReconciliationMoneyAmount());

                detailVOS.add(detailVO);
                taxSet.add(productVO.getTaxRate());
            }
        }
        if (payerSet.size()>1 || taxSet.size()>1){
            return Wrapper.fail(ResponseCode.SA_SELECT_SAME_PAYER_OR_TAX_RATE_ORDER);
        }
        dataVO.setCreateTime(LocalDateTime.now().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
        dataVO.setRows(detailVOS);

        return Wrapper.success(dataVO);
    }

    /**
     * 提交待请款单，生成请款单数据
     * @author Saske
     * @param sysUser applyVO
     * @return 对账单请款VO
     **/
    @Override
    public Wrapper<ApplyAmountSaveVO> generateApplyAmount(UserLoginCacheDTO sysUser, ReconciliationApplyVO applyVO) {
        // step 1: 获取对账单
        BusinessReconciliationDO entity = repository.findById(applyVO.getReconciliationId()).orElse(null);
        ApplyAmountSaveVO dataVO = new ApplyAmountSaveVO();
        if (entity == null){
            return Wrapper.success(dataVO);
        }

        // step 2:查询对公账户信息
        Wrapper<CorporateAccountConfigResponse> config = configService.corporateAccountConfig(entity.getVendorMemberId(), entity.getVendorRoleId());
        if (Optional.ofNullable(config).map(Wrapper::getCode).map(code -> code !=ResponseCode.SUCCESS.getCode()).orElse(true)) {
            Wrapper.fail(ResponseCode.SERVICE_ORDER_ERROR);
        }

        // step 3:获取对账单明细
        Specification<BusinessReconciliationRowDO> detailSpecification = (root, query, cb) -> cb.equal(root.get("reconciliation").as(BusinessReconciliationDO.class), entity);
        List<BusinessReconciliationRowDO> detailEntities = detailRepository.findAll(detailSpecification);
        // step 4: 查询订单类型的对应行明细
        // 查询所有订单id
        Set<Long> billIds = detailEntities.stream().filter(f -> Objects.equals(f.getBillType(), ApplyAmountRowBillTypeEnum.ORDER.getCode())).map(BusinessReconciliationRowDO::getOrderId).collect(Collectors.toSet());
        OrderFeignIdsVO orderFeignIdsVO = new OrderFeignIdsVO();
        orderFeignIdsVO.setOrderIds(Lists.newArrayList(billIds));
        Wrapper<List<OrderBuyerSettleReconciliationFeignVO>> wrapper = feignService.settleReconciliationBuyerOrders(orderFeignIdsVO);
        if (wrapper.getCode() != ResponseCode.SUCCESS.getCode() || CollectionUtils.isEmpty(wrapper.getData())) {
           return Wrapper.fail(ResponseCode.SERVICE_ORDER_ERROR);
        }
        List<OrderBuyerSettleReconciliationFeignVO> orderHelpDataList = wrapper.getData();
        // step 5: 查询待对账退货订单
        // 获取订单
        List<Long> returnIds = detailEntities.stream().filter(f -> Objects.equals(f.getBillType(), ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode())).map(BusinessReconciliationRowDO::getOrderId).collect(Collectors.toList());
        List<SettlementProductDO> settlementProductDOList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(returnIds)) {
            Specification<SettlementOrderDO> specification = (root, query, builder) -> {
                CriteriaBuilder.In<Long> id = builder.in(root.get("billId").as(Long.class));
                returnIds.forEach(id::value);
                Predicate billType = builder.equal(root.get("billType").as(Integer.class), ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode());
                return query.where(billType, id).getRestriction();
            };
            List<SettlementOrderDO> settlementOrderList = orderRepository.findAll(specification);
            List<SettlementProductDO> settlementProductDOHelpList = settlementOrderList.stream().flatMap(map -> map.getProducts().stream()).collect(Collectors.toList());
            settlementProductDOList.addAll(settlementProductDOHelpList);
        }
        // step 6: 组装请款明细项
          List<ApplyAmountDetailBO> applyAmountDetails = new ArrayList<>();
        if (!CollectionUtils.isEmpty(billIds)) {
            QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
            QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
            List<ApplyAmountDetailBO> tempAmountDetails = jpaQueryFactory.select(Projections.constructor(ApplyAmountDetailBO.class,
                            applyAmountRowDO.billId,
                            applyAmountRowDO.billType,
                            applyAmountRowDO.taxRate,
                            applyAmountDO.status,
                            applyAmountRowDO.applyPayment))
                    .from(applyAmountDO)
                    .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                    .where(applyAmountRowDO.billId.in(billIds)).fetch();
            applyAmountDetails.addAll(tempAmountDetails);
        }

        // 查询已付款、非物料对账单、根据订单id、来源合同id得数据
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        List<Long> sourceContractIds = detailEntities.stream().map(BusinessReconciliationRowDO::getSourceContractId).filter(Objects::nonNull).filter(sourceContractId -> sourceContractId.compareTo(0L) > 0).collect(Collectors.toList());
        billIds.addAll(sourceContractIds);
        List<com.querydsl.core.types.Predicate>  predicateList = new ArrayList<>();
        predicateList.add(applyAmountRowDO.billId.in(billIds));
        if (!CollectionUtils.isEmpty(sourceContractIds)) {
            predicateList.add(applyAmountRowDO.sourceContractId.in(sourceContractIds));
        }
        com.querydsl.core.types.Predicate[] predicates = new com.querydsl.core.types.Predicate[predicateList.size()];
        predicateList.toArray(predicates);
        List<ApplyAmountRowDO> applyAmountRowDOList = jpaQueryFactory.select(applyAmountRowDO).distinct()
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                .where(applyAmountDO.status.eq(ApplyAmountStatusEnum.PAID.getCode()),
                        applyAmountDO.applyType.ne(ApplyAmountTypeEnum.MATERIAL.getCode()),
                        applyAmountRowDO.applyPayment.gt(applyAmountRowDO.forWriteOffAmount))
                .where(ExpressionUtils.anyOf(predicates)).fetch();


        // 聚合 订单id + 订单类型 + 税率的 对账明细项 初步组装
        List<ApplyAmountRowSaveVO> tempDetailVOS = new ArrayList<>(detailEntities.stream().map(map -> {
            ApplyAmountRowSaveVO detailVo = new ApplyAmountRowSaveVO();
            // 单据id
            detailVo.setBillId(map.getOrderId());
            // 单据编号
            detailVo.setBillNo(map.getOrderNo());
            // 单据摘要
            detailVo.setBillAbstract(Optional.ofNullable(map.getBillAbstract()).orElse(entity.getReconciliationAbstract()));
            // 单据类型
            detailVo.setBillType(map.getBillType());
            // 单据来源合同id
            detailVo.setSourceContractId(Optional.ofNullable(map.getSourceContractId()).orElse(0L));
            // 订单类型名称
            detailVo.setBillTypeName(ApplyAmountRowBillTypeEnum.getNameByCode(map.getBillType()));
            // 单据日期
            detailVo.setBillTime(map.getBillDate().format(SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER));
            // 单据状态
            detailVo.setBillStatus(map.getOrderStatus());
            // 单据金额
            // 单据金额 如果是订单类型，从订单查询过滤数据，如果是退货，则从退货售后待对账汇总
            BigDecimal billAmount = Objects.equals(map.getBillType(), ApplyAmountRowBillTypeEnum.ORDER.getCode())
                    ? orderHelpDataList.stream().filter(f -> Objects.equals(map.getOrderId(), f.getOrderId()) && f.getTaxRate().multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).compareTo(map.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0).findFirst().map(OrderBuyerSettleReconciliationFeignVO::getProductAmount).orElse(BigDecimal.ZERO)
                    : settlementProductDOList.stream().filter(f ->
                    // 合同id、单据id、税率一直
                    Objects.equals(map.getSourceContractId(), detailVo.getSourceContractId())
                            && Objects.equals(map.getOrderId(), f.getOrder().getBillId())
                            && map.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(f.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0)
                    .map( map1 -> map1.getPrice().multiply(map1.getReceiveQuantity()).negate()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
            detailVo.setBillAmount(billAmount);
            detailVo.setBillRowAmount(billAmount);
            Integer hasTax = map.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(new BigDecimal("0.00")) == 0
                    ?  CommonBooleanEnum.NO.getCode() : CommonBooleanEnum.YES.getCode();
            String hasTaxName = map.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(new BigDecimal("0.00")) == 0
                    ? CommonBooleanEnum.NO.getMessage() : CommonBooleanEnum.YES.getMessage();
            // 含税
            detailVo.setHasTax(hasTax);
            detailVo.setHasTaxName(hasTaxName);
            // 税率
            detailVo.setTaxRate(map.getTaxRate());
            // 已付款
            detailVo.setPaid(new BigDecimal("0"));
            // 已请款待付款
            detailVo.setAppliedUnpaid(new BigDecimal("0"));
            // 对账金额
            detailVo.setReconciliationAmount(map.getCurrentMoney());
            // 请款金额
            detailVo.setApplyPayment(map.getCurrentMoney());
            // 核销金额
            detailVo.setWriteOffAmount(new BigDecimal("0"));
            // 是否存在请款付款后，申请金额大于用于核销金额、且满足的单据类型不是退货申请、订单或来源合同id、税率、
            boolean canWriteOffFlag = applyAmountRowDOList.stream()
                    .filter(f -> {
                        boolean res1 = !detailVo.getBillType().equals(ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode());
                        boolean res2 = f.getBillId().equals(detailVo.getBillId()) || (detailVo.getSourceContractId().compareTo(0L) > 0 && (f.getSourceContractId().equals(detailVo.getSourceContractId()) || f.getBillId().equals(detailVo.getSourceContractId())));
                        boolean res3 = f.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(detailVo.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0;
                        return res1 && res2 && res3;
                    }).count() > 0L;
            detailVo.setCanWriteOffFlag(canWriteOffFlag);
            return detailVo;
        }).collect(Collectors.toMap(
                k -> "" + k.getBillId() + k.getBillType() + k.getTaxRate().setScale(2, RoundingMode.HALF_UP),
                v -> v,
                (existingValue, newValue) -> {
                    BigDecimal reconciliationAmount = existingValue.getReconciliationAmount()
                            .add(newValue.getReconciliationAmount());
                    existingValue.setReconciliationAmount(reconciliationAmount);
                    existingValue.setApplyPayment(reconciliationAmount);
                    return existingValue;
                })).values());

        // 填充已付款、已请款待付款
        List<ApplyAmountRowSaveVO> rowVOList = tempDetailVOS.stream().peek(peek -> {
            BigDecimal paidAmount = applyAmountDetails.stream().filter(f -> peek.getBillId().equals(f.getBillId())
                    && peek.getBillType().equals(f.getBillType())
                    && peek.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(f.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0
                    && ApplyAmountInnerStatusEnum.PAID.getCode().equals(f.getStatus())).map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal appliedNotPayAmount = applyAmountDetails.stream().filter(f -> peek.getBillId().equals(f.getBillId())
                            && peek.getBillType().equals(f.getBillType())
                            && peek.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(f.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0
                            && !ApplyAmountInnerStatusEnum.PAID.getCode().equals(f.getStatus())).map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            peek.setPaid(paidAmount);
            peek.setAppliedUnpaid(appliedNotPayAmount);

        }).collect(Collectors.toList());

        dataVO.setVendorMemberId(entity.getVendorMemberId());
        dataVO.setVendorRoleId(entity.getVendorRoleId());
        dataVO.setBuyerMemberId(entity.getBuyerMemberId());
        dataVO.setBuyerRoleId(entity.getBuyerRoleId());
        dataVO.setApplyType(ApplyAmountTypeEnum.MATERIAL.getCode());
        dataVO.setApplyTypeName(ApplyAmountTypeEnum.MATERIAL.getMessage());
        dataVO.setApplyAbstract(entity.getReconciliationAbstract());
        dataVO.setPayee(entity.getPayee());
        dataVO.setPayer(entity.getPayer());
        dataVO.setAccountName(Optional.ofNullable(config).map(Wrapper::getData).map(CorporateAccountConfigResponse::getName).orElse(""));
        dataVO.setBankAccount(Optional.ofNullable(config).map(Wrapper::getData).map(CorporateAccountConfigResponse::getBankAccount).orElse(""));
        dataVO.setBankDeposit(Optional.ofNullable(config).map(Wrapper::getData).map(CorporateAccountConfigResponse::getBankDeposit).orElse(""));
        dataVO.setExpectPayTime(LocalDateTime.now().plusDays(1).format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
        dataVO.setReconciliationId(entity.getId());
        dataVO.setReconciliationNo(entity.getReconciliationNo());
        dataVO.setRows(rowVOList);
        return Wrapper.success(dataVO);
    }

    /**
     * 新增对账单,对账单初始状态为待提交
     * @param sysUser saveVo
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Long> save(UserLoginCacheDTO sysUser, ReconciliationSaveVo saveVo) {
        // step1: 校验
        Wrapper<Void> checkWrapper = checkSave(saveVo);
        if (checkWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(ResponseCode.getByCode(checkWrapper.getCode()));
        }
        // step2: 组装对账单表数据
        BusinessReconciliationDO entity = buildEntity(saveVo, sysUser);
        // step3: 保存数据
        // 新增对账单附件
        fileService.addReconciliationFile(entity.getReconciliationNo(),saveVo.getFiles());
        // 新增业务对账
        repository.saveAndFlush(entity);
        // step4: 更新待对账单数据
        // 新增对账单，将对账数量，对账金额回写到待对账商品明细
        addToBusinessReconciliation(entity);
        // step5: 启动流程实例
        Wrapper<ComplexTaskCompleteVO> wrapper = feignService.startBusinessReconciliation(entity.getVendorMemberId(), entity.getVendorRoleId(), entity.getId());
        // 启动流程实例失败回滚
        if (Optional.ofNullable(wrapper).map(w -> w.getCode()!=ResponseCode.SUCCESS.getCode()).orElse(true)) {
            // 回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(ResponseCode.SERVICE_WORKFLOW_ERROR);
        }
        // 更新外部状态和工作流任务id
        ComplexTaskCompleteVO workStack = wrapper.getData();
        entity.setTaskId(workStack.getTaskId());
        entity.setOuterStatus(workStack.getOuterStatus());
        entity.setOtherNextStep(workStack.getOuterStep());
        repository.saveAndFlush(entity);

        return Wrapper.success(entity.getId());
    }

    /**
     * 由对账单前端数据 转化 数据库对应对账单保存实体
     * @param saveVo 保存对账单数据
     * @param sysUser 登录用户
     * @return 数据库对应对账单保存实体
     */
    private BusinessReconciliationDO buildEntity(ReconciliationSaveVo saveVo , UserLoginCacheDTO sysUser) {
        BusinessReconciliationDO entity = new BusinessReconciliationDO();
        List<ReconciliationRowSaveVO> details = saveVo.getRows();
        // 生成对账单号
        String reconciliationNo = SerialNumberUtils.getReconciliationNo(redisStringUtils, Constants.SETTLEMENT_RECONCILIATION_NO);
        // 组装业务对账单数据
        entity.setReconciliationNo(reconciliationNo); // 对账单号
        entity.setStatus(ReconciliationStatusEnum.TO_SUBMIT.getCode()); // 对账单状态 新增默认待提交
        entity.setOuterStatus(ReconciliationOuterStatusEnum.WAIT_SUBMIT.getCode()); // 外部状态 新增默认待提交
        entity.setReconciliationType(ReconciliationTypeEnum.MATERIAL.getCode()); // 外部状态 新增默认物料对账单
        entity.setReconciliationAbstract(saveVo.getReconciliationAbstract()); // 对账单摘要
        entity.setPayer(saveVo.getPayer()); // 付款方 - 采购商名称
        entity.setPayee(saveVo.getPayee()); // 收款方 - 供应商名称
        entity.setLaunchReconciliation(saveVo.getLaunchReconciliation()); // 发起对帐方

        entity.setRemark(saveVo.getRemark()); // 备注
        entity.setCreateTime(LocalDateTime.now()); // 单据时间
        entity.setBuyerMemberId(saveVo.getBuyerMemberId()); // 买方（采购）会员Id
        entity.setBuyerRoleId(saveVo.getBuyerRoleId()); // 采购方）会员角色Id
        entity.setVendorMemberId(sysUser.getMemberId()); // 所属会员Id
        entity.setVendorRoleId(sysUser.getMemberRoleId()); // 会员角色Id
        // 组装对账单明细列表
        List<BusinessReconciliationRowDO> rowList = buildDetails(details, entity);
        entity.setRows(rowList);
        // 汇总对账金额
        BigDecimal amount = rowList.stream().map(BusinessReconciliationRowDO::getCurrentMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        entity.setReconciliationMoneyAmount(amount);
        return entity;
    }

    /**
     * 检查保存
     * @param saveVo 保存对账单
     * @return 查询条件
     */
    private Wrapper<Void> checkSave(ReconciliationSaveVo saveVo) {
        List<ReconciliationRowSaveVO> details = saveVo.getRows();
        // 本次对账数量不能大于待对账数量
        long count = details.stream().filter(f -> f.getReconciliationQuantity().compareTo(f.getCurrentReconciliationQuantity()) < 0).count();
        if (count>0) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_CURRENT_RECONCILIATION_QUANTITY_GREATER_THAN_RECONCILIATION_QUANTITY);
        }
        // 必须一样税率
        long taxRateCount = details.stream().map(f -> f.getTaxRate().setScale(2, RoundingMode.HALF_UP).toString()).distinct().count();
        if (taxRateCount > 1) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_TAX_RATE_INCONSISTENT);
        }
        // 对账金额汇总必须大于0
        BigDecimal bigDecimal = details.stream().map(map -> {
            BigDecimal currentMoney = map.getPrice().multiply(map.getCurrentReconciliationQuantity());
            return map.getBillType().equals(ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode()) ? currentMoney.negate() : currentMoney;
        }).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        if (bigDecimal.compareTo(BigDecimal.ZERO) <= 0) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_TO_CURRENT_MONEY_LESS_THAN_ZERO);
        }

        return Wrapper.success();
    }

    /**
     * 构建新增明细项
     * @param details 对账单明细项
     * @param entity 对账单数据
     * @return 新增明细项
     */
    private List<BusinessReconciliationRowDO> buildDetails(List<ReconciliationRowSaveVO> details, BusinessReconciliationDO entity) {
        return details.stream().map(r -> {
            // 构建明细
            BusinessReconciliationRowDO row = new BusinessReconciliationRowDO();
            // 订单id
            row.setOrderId(r.getOrderId());
            // 对账单号
            row.setReconciliationNo(entity.getReconciliationNo());
            // 订单号
            row.setOrderNo(r.getOrderNo());
            // 订单状态
            row.setOrderStatus(r.getOrderStatus());
            // 单据类型
            row.setBillType(r.getBillType());
            // 订单类型
            row.setOrderType(r.getOrderType());
            // 单据日期
            row.setBillDate(LocalDate.parse(r.getBillDate(), SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER).atStartOfDay());
            // 单据金额
            row.setBillAmount(r.getBillAmount());
            // 单据摘要
            row.setBillAbstract(r.getBillAbstract());
            // 单据来源合同id
            row.setSourceContractId(r.getSourceContractId());
            // 结算商品明细数据来源ID
            row.setSettlementProductId(r.getProductId());
            // 预计结算日期
            row.setSettlementDate(LocalDate.parse(r.getExpectPayTime(), SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER).atStartOfDay());
            // 发货批次
            row.setDeliveryBatch(r.getDeliveryBatch());
            // 发货单号
            row.setDeliveryNo(r.getDeliveryNo());
            // 收货单号
            row.setReceiveNo(r.getReceiveNo());
            // 物料编码
            row.setProductNo(r.getProductNo());
            // 物料名称
            row.setName(r.getProductName());
            // 商品规格
            row.setSpec(r.getSpec());
            // 商品品类
            row.setCategory(r.getCategory());
            // 商品品牌
            row.setBrand(r.getBrand());
            // 单位
            row.setUnit(r.getUnit());
            // 税率（百分比的分子部分）
            row.setTaxRate(r.getTaxRate());
            // 单价（含税）
            row.setPrice(r.getPrice());
            // 待对账数量
            row.setReconciliationQuantity(r.getReconciliationQuantity());
            // 待对账金额（含税）
            BigDecimal reconciliationMoneyAmount = r.getPrice().multiply(r.getReconciliationQuantity());
            reconciliationMoneyAmount = r.getBillType().equals(ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode()) ? reconciliationMoneyAmount.negate() : reconciliationMoneyAmount;
            row.setReconciliationMoneyAmount(reconciliationMoneyAmount);
            // 本次对账数量要大于等于0
            row.setCurrentQuantity(r.getCurrentReconciliationQuantity());
            // 本次对账金额（含税）
            BigDecimal currentMoney = r.getPrice().multiply(r.getCurrentReconciliationQuantity());
            currentMoney = r.getBillType().equals(ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode()) ? currentMoney.negate() : currentMoney;
                    row.setCurrentMoney(currentMoney);
            // 设置对账单主表
            row.setReconciliation(entity);
            return row;
        }).collect(Collectors.toList());
    }

    /**
     * 修改对账单,对账单修改后状态设置待提交
     * @param sysUser saveVo
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Long> update(UserLoginCacheDTO sysUser, ReconciliationUpdateVo updateVo) {
        // step1: 更新校验
        // 查询对账单
        BusinessReconciliationDO entity = repository.findById(updateVo.getReconciliationId()).orElse(null);
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        Wrapper<Void> updateWrapper = checkUpdate(updateVo, entity);
        if (updateWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(ResponseCode.getByCode(updateWrapper.getCode()));
        }
        // step2: 更新数据库
        // 提交对账单，首先将原有行明细的对账数量，对账金额回写到待对账商品明细
        subtractToBusinessReconciliation(entity);
        // step3: 更新数据数据
        entity.setStatus(ReconciliationStatusEnum.TO_SUBMIT.getCode());
        entity.setReconciliationType(ReconciliationTypeEnum.MATERIAL.getCode());
        entity.setReconciliationAbstract(updateVo.getReconciliationAbstract());
        entity.setRemark(updateVo.getRemark());
        List<BusinessReconciliationRowDO> rowList = buildDetails(updateVo.getRows(), entity);
        entity.setRows(rowList);
        BigDecimal amount = CollectionUtils.isEmpty(updateVo.getRows()) ? BigDecimal.ZERO : rowList.stream().map(BusinessReconciliationRowDO::getCurrentMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        entity.setReconciliationMoneyAmount(amount);
        //删除原有对账单行明细数据
        detailRepository.deleteByReconciliationId(entity.getId());
        //删除原有对账单附件信息
        fileService.deleteReconciliationFile(updateVo.getReconciliationNo());
        repository.saveAndFlush(entity);
        //重新保存对账单附件信息
        fileService.addReconciliationFile(updateVo.getReconciliationNo(),updateVo.getFiles());
        //提交对账单，重新将对账数量，对账金额回写到待对账商品明细
        addToBusinessReconciliation(entity);
        return Wrapper.success(entity.getId());
    }

    /**
     * 新增更新待对账单
     * @param entity 对账单
     */
    private void addToBusinessReconciliation(BusinessReconciliationDO entity) {
        List<BusinessReconciliationRowDO> afterRows = detailRepository.findByReconciliationNo(entity.getReconciliationNo());
        afterRows.forEach(r -> {
            SettlementProductDO productDO = productRepository.findById(r.getSettlementProductId()).orElse(null);
            if (productDO != null) {
                // 已对账数量
                BigDecimal reconciledQuantity = productDO.getReconciledQuantity().add(r.getCurrentQuantity());
                productDO.setReconciledQuantity(reconciledQuantity);
                // 是否是售后退货
                BigDecimal reconciledMoney = productDO.getReconciledMoney().add(r.getCurrentMoney());
                productDO.setReconciledMoney(reconciledMoney);
                productDO.setReconciliationQuantity(productDO.getReconciliationQuantity().subtract(r.getCurrentQuantity()));
                productRepository.saveAndFlush(productDO);
            }
        });
    }

    /**
     * 删除回复待对账单
     * @param entity 对账单
     */
    private void subtractToBusinessReconciliation(BusinessReconciliationDO entity) {
        List<BusinessReconciliationRowDO> rows = entity.getRows();
        rows.forEach(r -> {
            SettlementProductDO productDO = productRepository.findById(r.getSettlementProductId()).orElse(null);
            if (productDO != null) {
                productDO.setReconciledQuantity(productDO.getReconciledQuantity().subtract(r.getCurrentQuantity()));
                // 是否是售后退货
                BigDecimal reconciledMoney = productDO.getReconciledMoney().subtract(r.getCurrentMoney());
                productDO.setReconciledMoney(reconciledMoney);
                productDO.setReconciliationQuantity(productDO.getReconciliationQuantity().add(r.getCurrentQuantity()));
                productRepository.saveAndFlush(productDO);
            }
        });
    }

    /**
     * 检查更新对账单
     * @param updateVo 更新数据
     * @param entity 数据库旧数据
     * @return 检查结果
     */
    private Wrapper<Void> checkUpdate(ReconciliationUpdateVo updateVo, BusinessReconciliationDO entity) {
        // step1:校验状态 对账单状态存在，且不是提交状态或不是确认不通过
        if (Optional.ofNullable(entity.getStatus())
                .map( status -> !(status.equals(ReconciliationStatusEnum.TO_SUBMIT.getCode())  // 待提交状态
                        || status.equals(ReconciliationStatusEnum.REJECT.getCode()))    // 确认不通过
                ).orElse(true)) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_UPDATE);
        }
        // step2:税率校验
        long taxRateCount = updateVo.getRows().stream().map(f -> f.getTaxRate().setScale(2, RoundingMode.HALF_UP).toString()).distinct().count();
        if (taxRateCount > 1) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_TAX_RATE_INCONSISTENT);
        }
        // 对账金额汇总必须大于0
        BigDecimal bigDecimal = updateVo.getRows().stream().map(map -> {
            BigDecimal currentMoney = map.getPrice().multiply(map.getCurrentReconciliationQuantity());
            return map.getBillType().equals(ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode()) ? currentMoney.negate() : currentMoney;
        }).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        if (bigDecimal.compareTo(BigDecimal.ZERO) <= 0) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_TO_CURRENT_MONEY_LESS_THAN_ZERO);
        }

        // step3: 校验对账数量没有超过总收货数量
        return checkCurrentQuantity(updateVo.getRows(), entity);
    }

    /**
     * 删除待新增对账单
     * @param reconciliationVo      对账单操作VO
     */
    @Override
    @Transactional(rollbackFor = BusinessException.class)
    public Wrapper<Long> delete(UserLoginCacheDTO sysUser, ReconciliationSubmitVo reconciliationVo) {
        BusinessReconciliationDO entity = repository.findById(reconciliationVo.getId()).orElse(null);
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        List<StatementCirculationRecordVO> recordList = recordService.getRecordList(entity.getReconciliationNo());
        if (!CollectionUtils.isEmpty(recordList)){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_CAN_NOT_DELETE);
        }
        //删除对账单，首先将原有行明细的对账数量，对账金额回写到待对账商品明细
        subtractToBusinessReconciliation(entity);
        // 删除附件
        fileService.deleteReconciliationFile(entity.getReconciliationNo());
        // 删除明细
        List<BusinessReconciliationRowDO> details = entity.getRows();
        detailRepository.deleteAll(details);
        // 删除对账单
        repository.delete(entity);
        return Wrapper.success(entity.getId());
    }

    /**
     * 检查明细填写的本次对账，不能超过收货数量-其他累计对账数
     * @param updateDetails 检查明细
     * @param entity 对账单
     */
    private Wrapper<Void> checkCurrentQuantity(List<ReconciliationRowSaveVO> updateDetails, BusinessReconciliationDO entity) {
        // 1.获取选择待对账明细项ids
        Set<Long> settlementProductIds = updateDetails.stream().map(ReconciliationRowSaveVO::getProductId).collect(Collectors.toSet());
        // 2.获取 选取对账的记录 （来源待对账列表中的明细项列表）
        List<SettlementProductDO> settlementProductList = productRepository.findAllById(settlementProductIds);
        if (CollectionUtils.isEmpty(settlementProductList)) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_CURRENT_RECONCILIATION_QUANTITY_GREATER_THAN_RECONCILIATION_QUANTITY);
        }

        // 3.获取 对应对账记录对账记录（排除当前修改的记录）
        Specification<BusinessReconciliationRowDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            CriteriaBuilder.In<Long> settlementProductIdIn = criteriaBuilder.in(root.get("settlementProductId").as(Long.class));
            for (Long settlementProductId:
                    settlementProductIds) {
                settlementProductIdIn.value(settlementProductId);
            }
            list.add(settlementProductIdIn);
            if (Objects.nonNull(entity.getId())) {
                list.add(criteriaBuilder.notEqual(root.get("reconciliation").as(BusinessReconciliationDO.class), entity));
            }
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };
        List<BusinessReconciliationRowDO> otherDetails = detailRepository.findAll(specification);
        List<BusinessReconciliationRowDO> finalOtherDetails = CollectionUtils.isEmpty(otherDetails) ? new ArrayList<>() : otherDetails;
        boolean count = updateDetails.stream().anyMatch(f -> {
            // 收货数量（可对账总数）
            BigDecimal receiveQuantity = settlementProductList.stream()
                    .filter(item -> item.getId().equals(f.getProductId()))
                    .map(SettlementProductDO::getReceiveQuantity).findFirst()
                    .orElse(BigDecimal.ZERO);
            // 本次对账总数
            BigDecimal currentReconciliationQuantity = f.getCurrentReconciliationQuantity();
            // 其他对账总数
            BigDecimal otherReduceReconciliationQuantity = finalOtherDetails.stream()
                    .filter(item -> item.getSettlementProductId().equals(f.getProductId()))
                    .map(BusinessReconciliationRowDO::getCurrentQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            // 收货数量（可对账总数）- 本次对账总数 -其他对账总数 < 0
            return receiveQuantity.subtract(currentReconciliationQuantity)
                    .subtract(otherReduceReconciliationQuantity)
                    .setScale(2, RoundingMode.HALF_UP)
                    .compareTo(new BigDecimal("0.00")) < 0;
        });
        if (count) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_CURRENT_RECONCILIATION_QUANTITY_GREATER_THAN_RECONCILIATION_QUANTITY);
        }
        return Wrapper.success();
    }

    /**
     * 提交对账单
     * @param sysUser submitVo
     **/
    @Override
    @Transactional(rollbackFor = BusinessException.class)
    public Wrapper<Long> submitReconciliation(UserLoginCacheDTO sysUser, ReconciliationSubmitVo submitVo) {
        // 1.获取对账单
        BusinessReconciliationDO entity = repository.findById(submitVo.getId()).orElse(null);
        // 2.对账单校验
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        if(!entity.getStatus().equals(ReconciliationStatusEnum.TO_SUBMIT.getCode())){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_CAN_NOT_SUBMIT);
        }
        // 3.执行流程数据
        TaskExecuteVO request = new TaskExecuteVO();
        request.setProcessKey(ProcessEnum.SETTLE_ACCOUNTS_BUSINESS_RECONCILIATION.getProcessKey());
        request.setAgree(-1);
        request.setMemberId(entity.getVendorMemberId());
        request.setRoleId(entity.getVendorRoleId());
        request.setDataId(entity.getId());
        request.setTaskId(entity.getTaskId());
        Wrapper<ComplexTaskCompleteVO> wrapper = feignService.completeBusinessReconciliation(request);
        // 提交流程实例失败回滚
        if (Optional.ofNullable(wrapper).map(w -> w.getCode()!=ResponseCode.SUCCESS.getCode()).orElse(true)) {
            // 回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(ResponseCode.SERVICE_WORKFLOW_ERROR);
        }
        // 4.更新状态、外部状态、工作流任务id
        ComplexTaskCompleteVO workStack = wrapper.getData();
        entity.setStatus(ReconciliationStatusEnum.TO_CONFIRM.getCode());
        entity.setOuterStatus(workStack.getOuterStatus());
        entity.setTaskId(workStack.getTaskId());
        entity.setOtherNextStep(workStack.getOuterStep());
        repository.saveAndFlush(entity);
        // 5.添加流转记录
        recordService.addRecord(sysUser,entity.getReconciliationNo(),entity.getStatus(), ReconciliationOperationEnum.SUBMIT.getMessage(),null);

        return Wrapper.success();
    }

    /**
     * 确认对账单
     * @param sysUser submitVo
     **/
    @Override
    @Transactional(rollbackFor = BusinessException.class)
    public Wrapper<Long> confirmReconciliation(UserLoginCacheDTO sysUser, ReconciliationRejectConfirmVo confirmVo) {
        // 1.获取对账单
        BusinessReconciliationDO entity = repository.findById(confirmVo.getReconciliationId()).orElse(null);
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        // 2.校验状态
        if (!entity.getStatus().equals(ReconciliationStatusEnum.TO_CONFIRM.getCode())){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_CAN_NOT_CONFIRM);
        }
        TaskExecuteVO request = new TaskExecuteVO();
        request.setProcessKey(ProcessEnum.SETTLE_ACCOUNTS_BUSINESS_RECONCILIATION.getProcessKey());
        request.setMemberId(entity.getBuyerMemberId());
        request.setRoleId(entity.getBuyerRoleId());
        request.setDataId(entity.getId());
        request.setTaskId(entity.getTaskId());

        if (confirmVo.getIsConfirm().equals(CommonBooleanEnum.YES.getCode())){
            //对账单确认通过
            request.setAgree(1);
            Wrapper<ComplexTaskCompleteVO> wrapper = feignService.completeBusinessReconciliation(request);
            if (Optional.ofNullable(wrapper).map(w -> w.getCode()!=ResponseCode.SUCCESS.getCode()).orElse(true)) {
                // 回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return Wrapper.fail(ResponseCode.SERVICE_WORKFLOW_ERROR);
            }
            ComplexTaskCompleteVO workStack = wrapper.getData();
            entity.setStatus(ReconciliationStatusEnum.APPLY.getCode());
            entity.setOuterStatus(workStack.getOuterStatus());
            entity.setTaskId(workStack.getTaskId());
            entity.setOtherNextStep(workStack.getOuterStep());
            recordService.addRecord(sysUser,entity.getReconciliationNo(),entity.getStatus(), ReconciliationOperationEnum.CONFIRM.getMessage(),null);
        }else if (confirmVo.getIsConfirm().equals(CommonBooleanEnum.NO.getCode()) && StringUtils.hasLength(confirmVo.getReason())){
            //对账单确认不通过
            request.setAgree(0);
            Wrapper<ComplexTaskCompleteVO> wrapper = feignService.completeBusinessReconciliation(request);
            if (Optional.ofNullable(wrapper).map(w -> w.getCode()!=ResponseCode.SUCCESS.getCode()).orElse(true)) {
                // 回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return Wrapper.fail(ResponseCode.SERVICE_WORKFLOW_ERROR);
            }
            ComplexTaskCompleteVO workStack = wrapper.getData();
            entity.setStatus(ReconciliationStatusEnum.REJECT.getCode());
            entity.setOuterStatus(workStack.getOuterStatus());
            entity.setTaskId(workStack.getTaskId());
            entity.setOtherNextStep(workStack.getOuterStep());
            recordService.addRecord(sysUser,entity.getReconciliationNo(),entity.getStatus(), ReconciliationOperationEnum.REJECT.getMessage(),confirmVo.getReason());
        }else {
            //提醒用户输入确认不通过原因
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_REJECT_REASON);
        }

        repository.saveAndFlush(entity);
        return Wrapper.success();
    }

    /**
     * 查询对账单状态列表
     * @return 对账单状态列表
     */
    @Override
    public Wrapper<List<DropdownItem>> getStatusList(UserLoginCacheDTO sysUser) {
        return Wrapper.success(Arrays.stream(ReconciliationStatusEnum.values()).map(e -> new DropdownItem(e.getCode(), e.getMessage())).collect(Collectors.toList()));
    }

    /**
     * 查询待对账单结算方式列表
     * @return 待对账单结算方式列表
     */
    @Override
    public Wrapper<List<DropdownItem>> getPayTypeList(UserLoginCacheDTO sysUser) {
        return Wrapper.success(Arrays.stream(PurchaseContractPayTypeEnum.values()).map(e -> new DropdownItem(e.getCode(), e.getName())).collect(Collectors.toList()));
    }

    /**
     * 分页查询待新增对账单列表-供应商
     * @param sysUser       用户信息
     * @param toBeSaveReconciliationListVO          待新增对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<ToBeSubmitReconciliationVO>> pageCreatedReconciliation(UserLoginCacheDTO sysUser, ToBeSaveReconciliationListVO toBeSaveReconciliationListVO) {
        Pageable pageable = PageRequest.of(toBeSaveReconciliationListVO.getCurrent() -1, toBeSaveReconciliationListVO.getPageSize(), Sort.by("id").descending());
        Specification<BusinessReconciliationDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("vendorMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("vendorRoleId").as(Long.class), sysUser.getMemberRoleId()));
            //只查询供应商待提交，未通过的对账单
            list.add(criteriaBuilder.lessThan(root.get("status").as(Integer.class), ReconciliationStatusEnum.TO_CONFIRM.getCode()));

            //对账单号
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getReconciliationNo())){
                list.add(criteriaBuilder.like(root.get("reconciliationNo").as(String.class),"%"+toBeSaveReconciliationListVO.getReconciliationNo().trim()+"%"));
            }

            //对账单摘要
            if(StringUtils.hasLength(toBeSaveReconciliationListVO.getReconciliationAbstract())) {
                list.add(criteriaBuilder.like(root.get("reconciliationAbstract").as(String.class), "%" + toBeSaveReconciliationListVO.getReconciliationAbstract().trim() + "%"));
            }

            //付款方
            if(StringUtils.hasLength(toBeSaveReconciliationListVO.getPayer())) {
                list.add(criteriaBuilder.like(root.get("payer").as(String.class), "%" + toBeSaveReconciliationListVO.getPayer().trim() + "%"));
            }

            //对账单单据起始时间
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(toBeSaveReconciliationListVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //对账单单据结束时间
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(toBeSaveReconciliationListVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return this.buildPageCreatedReconciliation(repository.findAll(specification,pageable));
    }

    /**
     * 分页查询对账单列表-供应商
     * @param sysUser                用户信息
     * @param reconciliationListVO   对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<ToBeSubmitReconciliationVO>> pageVendorReconciliation(UserLoginCacheDTO sysUser,ReconciliationListVO reconciliationListVO) {
        Pageable pageable = PageRequest.of(reconciliationListVO.getCurrent() -1, reconciliationListVO.getPageSize(), Sort.by("id").descending());
        Specification<BusinessReconciliationDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("vendorMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("vendorRoleId").as(Long.class), sysUser.getMemberRoleId()));

            //对账单号
            if (StringUtils.hasLength(reconciliationListVO.getReconciliationNo())){
                list.add(criteriaBuilder.like(root.get("reconciliationNo").as(String.class),"%"+reconciliationListVO.getReconciliationNo().trim()+"%"));
            }

            //对账单摘要
            if(StringUtils.hasLength(reconciliationListVO.getReconciliationAbstract())) {
                list.add(criteriaBuilder.like(root.get("reconciliationAbstract").as(String.class), "%" + reconciliationListVO.getReconciliationAbstract().trim() + "%"));
            }

            //付款方
            if(StringUtils.hasLength(reconciliationListVO.getPayer())) {
                list.add(criteriaBuilder.like(root.get("payer").as(String.class), "%" + reconciliationListVO.getPayer().trim() + "%"));
            }

            //对账单状态
            if(NumberUtil.notNullOrZero(reconciliationListVO.getStatus())) {
                list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), reconciliationListVO.getStatus()));
            }

            //对账单单据起始时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //对账单单据结束时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return this.buildPageCreatedReconciliation(repository.findAll(specification,pageable));
    }

    /**
     * 分页查询待确认对账单列表-采购方
     * @param sysUser                           用户信息
     * @param toBeSaveReconciliationListVO      待确认对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<ToBeConfirmReconciliationVO>> pageConfirmReconciliation(UserLoginCacheDTO sysUser, ToBeSaveReconciliationListVO toBeSaveReconciliationListVO) {
        Pageable pageable = PageRequest.of(toBeSaveReconciliationListVO.getCurrent() -1, toBeSaveReconciliationListVO.getPageSize(), Sort.by("id").descending());
        Specification<BusinessReconciliationDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("buyerMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("buyerRoleId").as(Long.class), sysUser.getMemberRoleId()));
            //只查询供应商待确认的对账单
            list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), ReconciliationStatusEnum.TO_CONFIRM.getCode()));

            //对账单号
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getReconciliationNo())){
                list.add(criteriaBuilder.like(root.get("reconciliationNo").as(String.class),"%"+toBeSaveReconciliationListVO.getReconciliationNo().trim()+"%"));
            }

            //对账单摘要
            if(StringUtils.hasLength(toBeSaveReconciliationListVO.getReconciliationAbstract())) {
                list.add(criteriaBuilder.like(root.get("reconciliationAbstract").as(String.class), "%" + toBeSaveReconciliationListVO.getReconciliationAbstract().trim() + "%"));
            }

            //收款方
            if(StringUtils.hasLength(toBeSaveReconciliationListVO.getPayee())) {
                list.add(criteriaBuilder.like(root.get("payee").as(String.class), "%" + toBeSaveReconciliationListVO.getPayee().trim() + "%"));
            }

            //对账单单据起始时间
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(toBeSaveReconciliationListVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //对账单单据结束时间
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(toBeSaveReconciliationListVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return this.buildPageBuyerReconciliation(repository.findAll(specification,pageable));
    }

    /**
     * 分页查询待请款对账单列表-采购方
     * @param sysUser                           用户信息
     * @param toBeSaveReconciliationListVO      待确认对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<TobeApplyAmountListVO>> pageToBeApplyAmountReconciliation(UserLoginCacheDTO sysUser, ToBeSaveReconciliationListVO toBeSaveReconciliationListVO) {
        Pageable pageable = PageRequest.of(toBeSaveReconciliationListVO.getCurrent() -1, toBeSaveReconciliationListVO.getPageSize(), Sort.by("id").descending());
        Specification<BusinessReconciliationDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("buyerMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("buyerRoleId").as(Long.class), sysUser.getMemberRoleId()));
            //只查询待请款状态的对账单
            list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), ReconciliationStatusEnum.APPLY.getCode()));

            //对账单号
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getReconciliationNo())){
                list.add(criteriaBuilder.like(root.get("reconciliationNo").as(String.class),"%"+toBeSaveReconciliationListVO.getReconciliationNo().trim()+"%"));
            }

            //对账单摘要
            if(StringUtils.hasLength(toBeSaveReconciliationListVO.getReconciliationAbstract())) {
                list.add(criteriaBuilder.like(root.get("reconciliationAbstract").as(String.class), "%" + toBeSaveReconciliationListVO.getReconciliationAbstract().trim() + "%"));
            }

            //收款方
            if(StringUtils.hasLength(toBeSaveReconciliationListVO.getPayee())) {
                list.add(criteriaBuilder.like(root.get("payee").as(String.class), "%" + toBeSaveReconciliationListVO.getPayee().trim() + "%"));
            }

            //对账单单据起始时间
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(toBeSaveReconciliationListVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //对账单单据结束时间
            if (StringUtils.hasLength(toBeSaveReconciliationListVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(toBeSaveReconciliationListVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return this.buildPageToBeApplyAmountReconciliation(repository.findAll(specification,pageable));
    }

    /**
     * 分页查询对账单列表-采购方
     * @param sysUser                用户信息
     * @param reconciliationListVO   对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<ToBeConfirmReconciliationVO>> pageBuyerReconciliation(UserLoginCacheDTO sysUser,ReconciliationListVO reconciliationListVO) {
        Pageable pageable = PageRequest.of(reconciliationListVO.getCurrent() -1, reconciliationListVO.getPageSize(), Sort.by("id").descending());
        Specification<BusinessReconciliationDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("buyerMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("buyerRoleId").as(Long.class), sysUser.getMemberRoleId()));

            //对账单号
            if (StringUtils.hasLength(reconciliationListVO.getReconciliationNo())){
                list.add(criteriaBuilder.like(root.get("reconciliationNo").as(String.class),"%"+reconciliationListVO.getReconciliationNo().trim()+"%"));
            }

            //对账单摘要
            if(StringUtils.hasLength(reconciliationListVO.getReconciliationAbstract())) {
                list.add(criteriaBuilder.like(root.get("reconciliationAbstract").as(String.class), "%" + reconciliationListVO.getReconciliationAbstract().trim() + "%"));
            }

            //收款方
            if(StringUtils.hasLength(reconciliationListVO.getPayee())) {
                list.add(criteriaBuilder.like(root.get("payee").as(String.class), "%" + reconciliationListVO.getPayee().trim() + "%"));
            }

            //订单类型
            if(NumberUtil.notNullOrZero(reconciliationListVO.getStatus())) {
                list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), reconciliationListVO.getStatus()));
            }

            //对账单单据起始时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //对账单单据结束时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return this.buildPageBuyerReconciliation(repository.findAll(specification,pageable));
    }

    /**
     * 查询对账单详情
     * @param reconciliationDetailVO  对账单详情查询条件
     * @return 对账单详情
     */
    @Override
    public Wrapper<ToBeSubmitReconciliationDetailVO> getInfo(UserLoginCacheDTO sysUser, ReconciliationDetailVO reconciliationDetailVO) {
        // step1: 查询对账单
        BusinessReconciliationDO entity = repository.findById(reconciliationDetailVO.getReconciliationId()).orElse(null);
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        // step2: 查询开票记录
        Specification<BusinessReconciliationInvoiceRecordDO> invoiceRecordDOSpecification = (root, query, cb) -> cb.equal(root.get("reconciliation").as(BusinessReconciliationDO.class), entity);
        List<BusinessReconciliationInvoiceRecordDO> invoiceRecordEntities = invoiceRecordRepository.findAll(invoiceRecordDOSpecification);
        List<ReconciliationInvoiceNumber> numbers = invoiceRecordEntities.stream().map(map -> {
            ReconciliationInvoiceNumber item = new ReconciliationInvoiceNumber();
            item.setInvoiceNumber(map.getNumber());
            item.setInvoiceDate(map.getInvoiceDate());
            item.setInvoiceMoney(map.getInvoiceAmount());
            return item;
        }).collect(Collectors.toList());
        // step3: 组装开票信息
        ToBeSubmitReconciliationDetailVO detailVO = new ToBeSubmitReconciliationDetailVO();
        detailVO.setReconciliationId(entity.getId());
        detailVO.setReconciliationNo(entity.getReconciliationNo());
        detailVO.setStatus(entity.getStatus().toString());
        detailVO.setStatusName(ReconciliationStatusEnum.getNameByCode(entity.getStatus()));
        detailVO.setOutStatus(entity.getOuterStatus());
        detailVO.setOutStatusName(ReconciliationOuterStatusEnum.getNameByCode(entity.getOuterStatus()));
        detailVO.setReconciliationType(ReconciliationTypeEnum.getNameByCode(entity.getReconciliationType()));
        detailVO.setReconciliationAbstract(entity.getReconciliationAbstract());
        detailVO.setPayer(entity.getPayer());
        detailVO.setPayee(entity.getPayee());
        detailVO.setLaunchReconciliation(entity.getLaunchReconciliation());
        detailVO.setReconciliationMoneyAmount(entity.getReconciliationMoneyAmount());
        detailVO.setRemark(entity.getRemark());
        detailVO.setFiles(fileService.getFileList(entity.getReconciliationNo()));
        detailVO.setCreateTime(entity.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
        detailVO.setInvoiceMessages(null);
        detailVO.setRecords(recordService.getRecordList(entity.getReconciliationNo()));
        detailVO.setExternalLogStates(getLogOtherState(ProcessEnum.SETTLE_ACCOUNTS_BUSINESS_RECONCILIATION.getCode(),entity.getBuyerRoleId(), entity.getVendorRoleId(), entity.getOtherNextStep()));
        if (!CollectionUtils.isEmpty(numbers)) {
            ReconciliationInvoiceMessage invoiceMessages = new ReconciliationInvoiceMessage();
            invoiceMessages.setNumbers(numbers);
            detailVO.setInvoiceMessages(invoiceMessages);
        }
        return Wrapper.success(detailVO);
    }

    public List<LogOtherStateVO> getLogOtherState(Integer processEnum,Long roleId, Long subRoleId, Integer nextStep) {
        ExternalProcessQueryVO taskParamQueryVO = new ExternalProcessQueryVO();
        taskParamQueryVO.setProcessKey(ProcessEnum.getProcessKeyByCode(processEnum));
        taskParamQueryVO.setRoleId(roleId);
        taskParamQueryVO.setSubRoleId(subRoleId);
        Wrapper<SimpleProcessDefVO> listWrapper = feignService.getLogOtherState(taskParamQueryVO);
        if (ResponseCode.SUCCESS.getCode() != listWrapper.getCode()) {
            return new ArrayList<>();
        }
        List<SimpleTaskVO> data = listWrapper.getData().getTasks();
        return data.stream().map(taskStepQueryVO -> {
            LogOtherStateVO quotationState = new LogOtherStateVO();
            quotationState.setOperationalProcess(taskStepQueryVO.getTaskName());
            quotationState.setRoleName(taskStepQueryVO.getRoleName());
            quotationState.setIsExecute((null != nextStep && nextStep >= taskStepQueryVO.getTaskStep()) ? SettleAccountsCommon.IsExecute.YES : SettleAccountsCommon.IsExecute.NOT);
            quotationState.setState(taskStepQueryVO.getTaskStep());
            quotationState.setStateName(ReconciliationOuterStatusEnum.getNameByStep(taskStepQueryVO.getTaskStep()));
            return quotationState;
        }).collect(Collectors.toList());
    }

    /**
     * 对账单列表导出-供应商
     * @param reconciliationListVO 接口参数
     * @param response 响应体
     */
    @Override
    public void exportVendorReconciliation(HttpServletResponse response, ReconciliationListVO reconciliationListVO, String token) {

        UserLoginCacheDTO sysUser = this.needLoginFromBusinessPlatform(token);

        Specification<BusinessReconciliationDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("vendorMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("vendorRoleId").as(Long.class), sysUser.getMemberRoleId()));

            //对账单号
            if (StringUtils.hasLength(reconciliationListVO.getReconciliationNo())){
                list.add(criteriaBuilder.like(root.get("reconciliationNo").as(String.class),"%"+reconciliationListVO.getReconciliationNo().trim()+"%"));
            }

            //对账单摘要
            if(StringUtils.hasLength(reconciliationListVO.getReconciliationAbstract())) {
                list.add(criteriaBuilder.like(root.get("reconciliationAbstract").as(String.class), "%" + reconciliationListVO.getReconciliationAbstract().trim() + "%"));
            }

            //付款方
            if(StringUtils.hasLength(reconciliationListVO.getPayer())) {
                list.add(criteriaBuilder.like(root.get("payer").as(String.class), "%" + reconciliationListVO.getPayer().trim() + "%"));
            }

            //对账单状态
            if(NumberUtil.notNullOrZero(reconciliationListVO.getStatus())) {
                list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), reconciliationListVO.getStatus()));
            }

            //对账单单据起始时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //对账单单据结束时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        //供应商对账单信息
        List<BusinessReconciliationDO> entityList = repository.findAll(specification, Sort.by("id").descending());
        //供应商对账单行明细信息
        List<BusinessReconciliationRowDO> rowEntityList = detailRepository.findByReconciliationIn(entityList);

        List<Map<String,Object>> reconciliations = entityList.stream().map(entity -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("reconciliationNo",entity.getReconciliationNo());
            map.put("reconciliationAbstract",entity.getReconciliationAbstract());
            map.put("payer",entity.getPayer());
            map.put("reconciliationMoneyAmount",entity.getReconciliationMoneyAmount());
            map.put("createTime",entity.getCreateTime());
            map.put("status",ReconciliationStatusEnum.getNameByCode(entity.getStatus()));
            return map;
        }).collect(Collectors.toList());

        List<Map<String,Object>> reconciliationRows = rowEntityList.stream().map(entity -> {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("reconciliationNo",entity.getReconciliationNo());
            map.put("orderNo",entity.getOrderNo());
            map.put("settlementDate",entity.getSettlementDate());
            map.put("deliveryBatch",entity.getDeliveryBatch());
            map.put("deliveryNo",entity.getDeliveryNo());
            map.put("receiveNo",entity.getReceiveNo());
            map.put("productNo",entity.getProductNo());
            map.put("name",entity.getName());
            map.put("spec",entity.getSpec());
            map.put("category",entity.getCategory());
            map.put("brand",entity.getBrand());
            map.put("unit",entity.getUnit());
            map.put("taxRate",entity.getTaxRate());
            map.put("price",entity.getPrice());
            map.put("reconciliationQuantity",entity.getReconciliationQuantity());
            map.put("reconciliationMoneyAmount",entity.getReconciliationMoneyAmount());
            map.put("currentQuantity",entity.getCurrentQuantity());
            map.put("currentMoney",entity.getCurrentMoney());
            return map;
        }).collect(Collectors.toList());

        ExcelWriter writer = new ExcelWriter(false, BusinessReconciliationExcelEnum.FROM_NAME.getMessage());

        //自定义标题别名
        Map<String, String> headerAliasP = new LinkedHashMap<>();
        headerAliasP.put("reconciliationNo", BusinessReconciliationExcelEnum.RECONCILIATION_NO.getMessage());
        headerAliasP.put("reconciliationAbstract", BusinessReconciliationExcelEnum.RECONCILIATION_ABSTRACT.getMessage());
        headerAliasP.put("payer",  BusinessReconciliationExcelEnum.PAYER.getMessage());
        headerAliasP.put("reconciliationMoneyAmount",  BusinessReconciliationExcelEnum.RECONCILIATION_MONEY_AMOUNT.getMessage());
        headerAliasP.put("createTime", BusinessReconciliationExcelEnum.CREATE_TIME.getMessage());
        headerAliasP.put("status", BusinessReconciliationExcelEnum.STATUS.getMessage());
        writer.setHeaderAlias(headerAliasP);

        // 合并单元格后的标题行，使用默认标题样式
        writer.merge(5,  BusinessReconciliationExcelEnum.MERGE.getMessage());
        // 一次性写出内容，使用默认样式
        writer.write(reconciliations,true);
        //设置所有列为自动宽度，不考虑合并单元格
        writer.autoSizeColumnAll();
        writer.setSheet( BusinessReconciliationExcelEnum.SHEET.getMessage());
        //自定义标题别名
        Map<String, String> headerAliasE = new LinkedHashMap<>();
        headerAliasE.put("reconciliationNo",  BusinessReconciliationExcelEnum.RECONCILIATION_NO.getMessage());
        headerAliasE.put("orderNo",  BusinessReconciliationExcelEnum.ORDER_NO.getMessage());
        headerAliasE.put("settlementDate",  BusinessReconciliationExcelEnum.SETTLEMENT_DATE.getMessage());
        headerAliasE.put("deliveryBatch",  BusinessReconciliationExcelEnum.DELIVERY_BATCH.getMessage());
        headerAliasE.put("deliveryNo", BusinessReconciliationExcelEnum.DELIVERY_NO.getMessage());
        headerAliasE.put("receiveNo", BusinessReconciliationExcelEnum.RECEIVE_NO.getMessage());
        headerAliasE.put("productNo", BusinessReconciliationExcelEnum.PRODUCT_NO.getMessage());
        headerAliasE.put("name",  BusinessReconciliationExcelEnum.NAME.getMessage());
        headerAliasE.put("spec",  BusinessReconciliationExcelEnum.SPEC.getMessage());
        headerAliasE.put("category", BusinessReconciliationExcelEnum.CATEGORY.getMessage());
        headerAliasE.put("brand", BusinessReconciliationExcelEnum.BRAND.getMessage());
        headerAliasE.put("unit", BusinessReconciliationExcelEnum.UNIT.getMessage());
        headerAliasE.put("taxRate", BusinessReconciliationExcelEnum.TAX_RATE.getMessage());
        headerAliasE.put("price", BusinessReconciliationExcelEnum.PRICE.getMessage());
        headerAliasE.put("reconciliationQuantity", BusinessReconciliationExcelEnum.RECONCILIATION_QUANTITY.getMessage());
        headerAliasE.put("reconciliationMoneyAmount", BusinessReconciliationExcelEnum.RECONCILIATION_MONEY_AMOUNT_TOTAL.getMessage());
        headerAliasE.put("currentQuantity", BusinessReconciliationExcelEnum.CURRENT_QUANTITY.getMessage());
        headerAliasE.put("currentMoney", BusinessReconciliationExcelEnum.CURRENT_MONEY.getMessage());
        writer.setHeaderAlias(headerAliasE);
        // 合并单元格后的标题行，使用默认标题样式
        writer.merge(17, BusinessReconciliationExcelEnum.MERGE_DETAIL.getMessage());
        writer.write(reconciliationRows,true);
        //设置所有列为自动宽度，不考虑合并单元格
        writer.autoSizeColumnAll();

        String fileName = BusinessReconciliationExcelEnum.FILE_NAME.getMessage()+"-" + DateUtil.date() + ".xls";
        try (ServletOutputStream out = response.getOutputStream()) {
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            writer.flush(out, true);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    /**
     * 对账单列表导出-采购方
     * @param reconciliationListVO 接口参数
     * @param response 响应体
     */
    @Override
    public void exportBuyerReconciliation(HttpServletResponse response, ReconciliationListVO reconciliationListVO, String token) {

        UserLoginCacheDTO sysUser = this.needLoginFromBusinessPlatform(token);

        Specification<BusinessReconciliationDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("buyerMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("buyerRoleId").as(Long.class), sysUser.getMemberRoleId()));

            //对账单号
            if (StringUtils.hasLength(reconciliationListVO.getReconciliationNo())){
                list.add(criteriaBuilder.like(root.get("reconciliationNo").as(String.class),"%"+reconciliationListVO.getReconciliationNo().trim()+"%"));
            }

            //对账单摘要
            if(StringUtils.hasLength(reconciliationListVO.getReconciliationAbstract())) {
                list.add(criteriaBuilder.like(root.get("reconciliationAbstract").as(String.class), "%" + reconciliationListVO.getReconciliationAbstract().trim() + "%"));
            }

            //收款方
            if(StringUtils.hasLength(reconciliationListVO.getPayee())) {
                list.add(criteriaBuilder.like(root.get("payee").as(String.class), "%" + reconciliationListVO.getPayee().trim() + "%"));
            }

            //对账单状态
            if(NumberUtil.notNullOrZero(reconciliationListVO.getStatus())) {
                list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), reconciliationListVO.getStatus()));
            }

            //对账单单据起始时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //对账单单据结束时间
            if (StringUtils.hasLength(reconciliationListVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(reconciliationListVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        //供应商对账单信息
        List<BusinessReconciliationDO> entityList = repository.findAll(specification, Sort.by("id").descending());
        //供应商对账单行明细信息
        List<BusinessReconciliationRowDO> rowEntityList = detailRepository.findByReconciliationIn(entityList);

        List<Map<String,Object>> reconciliations = entityList.stream().map(entity -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("reconciliationNo",entity.getReconciliationNo());
            map.put("reconciliationAbstract",entity.getReconciliationAbstract());
            map.put("payee",entity.getPayee());
            map.put("reconciliationMoneyAmount",entity.getReconciliationMoneyAmount());
            map.put("createTime",entity.getCreateTime());
            map.put("status",ReconciliationStatusEnum.getNameByCode(entity.getStatus()));
            return map;
        }).collect(Collectors.toList());

        List<Map<String,Object>> reconciliationRows = rowEntityList.stream().map(entity -> {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("reconciliationNo",entity.getReconciliationNo());
            map.put("orderNo",entity.getOrderNo());
            map.put("settlementDate",entity.getSettlementDate());
            map.put("deliveryBatch",entity.getDeliveryBatch());
            map.put("deliveryNo",entity.getDeliveryNo());
            map.put("receiveNo",entity.getReceiveNo());
            map.put("productNo",entity.getProductNo());
            map.put("name",entity.getName());
            map.put("spec",entity.getSpec());
            map.put("category",entity.getCategory());
            map.put("brand",entity.getBrand());
            map.put("unit",entity.getUnit());
            map.put("taxRate",entity.getTaxRate());
            map.put("price",entity.getPrice());
            map.put("reconciliationQuantity",entity.getReconciliationQuantity());
            map.put("reconciliationMoneyAmount",entity.getReconciliationMoneyAmount());
            map.put("currentQuantity",entity.getCurrentQuantity());
            map.put("currentMoney",entity.getCurrentMoney());
            return map;
        }).collect(Collectors.toList());

        ExcelWriter writer = new ExcelWriter(false,BusinessReconciliationExcelEnum.FROM_NAME.getMessage());

        //自定义标题别名
        Map<String, String> headerAliasP = new LinkedHashMap<>();
        headerAliasP.put("reconciliationNo", BusinessReconciliationExcelEnum.RECONCILIATION_NO.getMessage());
        headerAliasP.put("reconciliationAbstract", BusinessReconciliationExcelEnum.RECONCILIATION_ABSTRACT.getMessage());
        headerAliasP.put("payee", BusinessReconciliationExcelEnum.PAYEE.getMessage());
        headerAliasP.put("reconciliationMoneyAmount", BusinessReconciliationExcelEnum.RECONCILIATION_MONEY_AMOUNT.getMessage());
        headerAliasP.put("createTime",BusinessReconciliationExcelEnum.CREATE_TIME.getMessage());
        headerAliasP.put("status",BusinessReconciliationExcelEnum.STATUS.getMessage());
        writer.setHeaderAlias(headerAliasP);

        // 合并单元格后的标题行，使用默认标题样式
        writer.merge(5, BusinessReconciliationExcelEnum.MERGE.getMessage());
        // 一次性写出内容，使用默认样式
        writer.write(reconciliations,true);
        //设置所有列为自动宽度，不考虑合并单元格
        writer.autoSizeColumnAll();
        writer.setSheet(BusinessReconciliationExcelEnum.SHEET.getMessage());
        //自定义标题别名
        Map<String, String> headerAliasE = new LinkedHashMap<>();
        headerAliasE.put("reconciliationNo", BusinessReconciliationExcelEnum.RECONCILIATION_NO.getMessage());
        headerAliasE.put("orderNo", BusinessReconciliationExcelEnum.ORDER_NO.getMessage());
        headerAliasE.put("settlementDate", BusinessReconciliationExcelEnum.SETTLEMENT_DATE.getMessage());
        headerAliasE.put("deliveryBatch", BusinessReconciliationExcelEnum.DELIVERY_BATCH.getMessage());
        headerAliasE.put("deliveryNo",BusinessReconciliationExcelEnum.DELIVERY_NO.getMessage());
        headerAliasE.put("receiveNo",BusinessReconciliationExcelEnum.RECEIVE_NO.getMessage());
        headerAliasE.put("productNo",BusinessReconciliationExcelEnum.PRODUCT_NO.getMessage());
        headerAliasE.put("name", BusinessReconciliationExcelEnum.NAME.getMessage());
        headerAliasE.put("spec", BusinessReconciliationExcelEnum.SPEC.getMessage());
        headerAliasE.put("category", BusinessReconciliationExcelEnum.CATEGORY.getMessage());
        headerAliasE.put("brand", BusinessReconciliationExcelEnum.BRAND.getMessage());
        headerAliasE.put("unit",BusinessReconciliationExcelEnum.UNIT.getMessage());
        headerAliasE.put("taxRate", BusinessReconciliationExcelEnum.TAX_RATE.getMessage());
        headerAliasE.put("price", BusinessReconciliationExcelEnum.PRICE.getMessage());
        headerAliasE.put("reconciliationQuantity", BusinessReconciliationExcelEnum.RECONCILIATION_QUANTITY.getMessage());
        headerAliasE.put("reconciliationMoneyAmount", BusinessReconciliationExcelEnum.RECONCILIATION_MONEY_AMOUNT_TOTAL.getMessage());
        headerAliasE.put("currentQuantity", BusinessReconciliationExcelEnum.CURRENT_QUANTITY.getMessage());
        headerAliasE.put("currentMoney", BusinessReconciliationExcelEnum.CURRENT_MONEY.getMessage());
        writer.setHeaderAlias(headerAliasE);
        // 合并单元格后的标题行，使用默认标题样式
        writer.merge(17, BusinessReconciliationExcelEnum.MERGE_DETAIL.getMessage());
        writer.write(reconciliationRows,true);
        //设置所有列为自动宽度，不考虑合并单元格
        writer.autoSizeColumnAll();

        String fileName = BusinessReconciliationExcelEnum.FILE_NAME.getMessage()+"-" + DateUtil.date() + ".xls";
        try (ServletOutputStream out = response.getOutputStream()) {
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            writer.flush(out, true);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    /**
     * 通过token获取用户登录信息
     * @param token HttpHeaders信息
     * @return 缓存的用户信息
     */
    @Override
    public UserLoginCacheDTO needLoginFromBusinessPlatform(String token) {
        //redis中是否有token
        Object tokenObj = redisUtils.get(token, Constants.REDIS_USER_INDEX);
        if (tokenObj == null) {
            throw new BusinessException(ResponseCode.TOKEN_DOES_NOT_EXIST_IN_CACHE);
        }

        return JSONUtil.parse(tokenObj).toBean(UserLoginCacheDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> addInvoiceRecord(Long reconciliationId, Long invoiceId, Long proofId, List<InvoiceProveBO> proveList) {
        // step1: 查询对账单
        BusinessReconciliationDO entity = repository.findById(reconciliationId).orElse(null);
        if (entity == null) {
           return  Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }

        // step2: 批量保存开票记录
        List<BusinessReconciliationInvoiceRecordDO> invoiceRecordEntities = proveList.stream().map(map -> {
            BusinessReconciliationInvoiceRecordDO item = new BusinessReconciliationInvoiceRecordDO();
            item.setReconciliation(entity);
            item.setInvoiceId(invoiceId);
            item.setProofId(proofId);
            item.setNumber(map.getNumber());
            item.setInvoiceDate(map.getInvoiceDate());
            item.setInvoiceAmount(map.getInvoiceAmount());
            return item;
        }).collect(Collectors.toList());
        invoiceRecordRepository.saveAll(invoiceRecordEntities);
        return Wrapper.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> pay(Long applyAmountId, Long reconciliationId) {

        // step1: 查询对账单
        BusinessReconciliationDO entity = repository.findById(reconciliationId).orElse(null);
        if (entity == null) {
            Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        // step2: 更新开票信息
        Specification<BusinessReconciliationInvoiceRecordDO> invoiceRecordSpecification = (root, query, cb) -> cb.equal(root.get("reconciliation").as(BusinessReconciliationDO.class), entity);
        List<BusinessReconciliationInvoiceRecordDO> invoiceRecordEntities = invoiceRecordRepository.findAll(invoiceRecordSpecification);
        List<Long> invoiceIdList = invoiceRecordEntities.stream().map(BusinessReconciliationInvoiceRecordDO::getInvoiceId).distinct().collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(invoiceIdList)) {
            // 先开票后付款
            invoiceService.pay(invoiceIdList, SettlementOrderTypeEnum.RECONCILIATION.getCode(), applyAmountId);
        } else {
            // 先付款后开票
            invoiceService.pay(new ArrayList<>(), SettlementOrderTypeEnum.RECONCILIATION.getCode(), applyAmountId);
        }
        // step3: 查询对账单明细
//        Specification<BusinessReconciliationRowDO> detailSpec = (root, query, cb) -> cb.equal(root.get("reconciliation").as(BusinessReconciliationDO.class), entity);
//        List<BusinessReconciliationRowDO> detailEntities = detailRepository.findAll(detailSpec);
        List<BusinessReconciliationRowDO> detailEntities =  Optional.ofNullable(entity).map(BusinessReconciliationDO ::getRows).orElse(new ArrayList<>());
        // step4: 查询待对账单主表
        // 查询对应的待对账明细列表
        List<Long> settlementProductIds = detailEntities.stream().map(BusinessReconciliationRowDO::getSettlementProductId).distinct().collect(Collectors.toList());
        Specification<SettlementProductDO> partSettlementProductSpec = (root, query, cb) -> {
            CriteriaBuilder.In<Long> in = cb.in(root.get("id").as(Long.class));
            settlementProductIds.forEach(in::value);
            return in;
        };
        List<SettlementProductDO> partSettlementProductEntities = productRepository.findAll(partSettlementProductSpec);
        // 过滤汇总对应的待对账主表
        List<SettlementOrderDO> allSettlementOrderEntities = partSettlementProductEntities.stream().map(SettlementProductDO::getOrder).filter(f -> f.getBillType().equals(ApplyAmountRowBillTypeEnum.ORDER.getCode())).collect(Collectors.toList());
        // step4: 查询待对账单子表
//        Specification<SettlementProductDO> allSettlementProductSpec = (root, query, cb) -> {
//            CriteriaBuilder.In<SettlementOrderDO> in = cb.in(root.get("order").as(SettlementOrderDO.class));
//            allSettlementOrderEntities.forEach(in::value);
//            return in;
//        };
//        List<SettlementProductDO> allSettlementProductEntities = productRepository.findAll(allSettlementProductSpec);
        List<SettlementProductDO> allSettlementProductEntities = allSettlementOrderEntities.stream().flatMap(map -> map.getProducts().stream()).collect(Collectors.toList());

        // step5: 根据待对账单子表查询对账单
        // 查询对账单明细
        List<Long> allSettlementProductIds = allSettlementProductEntities.stream().map(SettlementProductDO::getId).collect(Collectors.toList());
        Specification<BusinessReconciliationRowDO> detailSpec2 = (root, query, cb) -> {
            CriteriaBuilder.In<Long> in = cb.in(root.get("settlementProductId").as(Long.class));
            allSettlementProductIds.forEach(in::value);
            return in;
        };
        List<BusinessReconciliationRowDO> allDetailEntities = detailRepository.findAll(detailSpec2);
        // 对账单id集合
        List<Long> reconciliationIds = allDetailEntities.stream().map(BusinessReconciliationRowDO::getReconciliation).map(BusinessReconciliationDO::getId).collect(Collectors.toList());
        // step6: 查询对账单id对应的请款单且是已支付的
        Specification<ApplyAmountDO> applyAmountSpec = (root, query, cb) -> {
            Predicate status = cb.equal(root.get("status").as(Integer.class), ApplyAmountStatusEnum.PAID.getCode());
            CriteriaBuilder.In<Long> in = cb.in(root.get("reconciliationId").as(Long.class));
            reconciliationIds.forEach(in::value);
            return cb.and(status, in);
        };
        List<ApplyAmountDO> applyAmountEntities = applyAmountRepository.findAll(applyAmountSpec);
        // step7: 待对账单主表,过滤对账单子表待对账数量都为0的
        List<SettlementOrderDO> targetSettlementOrderEntities = allSettlementOrderEntities.stream().filter(f -> {
            // 查询对应的待对账明细中有没有待对账数量不为0的
            long count = allSettlementProductEntities.stream()
                    .filter(f2 -> f2.getOrder().getId().equals(f.getId())
                            && f2.getReconciliationQuantity().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(new BigDecimal("0.00").setScale(2, RoundingMode.HALF_UP)) > 0)
                    .count();
            if (count > 0) return false;
            // 过滤获取对应待对账的明细ids
            Set<Long> tempProductIds = allSettlementProductEntities.stream()
                    .filter(f2 -> f2.getOrder().getId().equals(f.getId()))
                    .map(SettlementProductDO::getId).collect(Collectors.toSet());
            // 根据应待对账的明细ids 过滤 获取对账单ids
            Set<Long> tempReconciliationIds = allDetailEntities.stream().filter(f2 -> tempProductIds.contains(f2.getSettlementProductId())).map(BusinessReconciliationRowDO::getReconciliation).map(BusinessReconciliationDO::getId).collect(Collectors.toSet());
            // 获取由对应对账单生成的请款单已付款的记录数
            long count1 = applyAmountEntities.stream().filter(f2 -> tempReconciliationIds.contains(f2.getReconciliationId())).count();
            return tempReconciliationIds.size() >= count1;
        }).collect(Collectors.toList());
        // step8: 更新对应订单支付批次已结算
        if (!CollectionUtils.isEmpty(targetSettlementOrderEntities)) {
            targetSettlementOrderEntities.forEach(item -> {
                OrderUpdateDeliveriesFeignVO request = new OrderUpdateDeliveriesFeignVO();
                request.setOrderId(item.getBillId());
                request.setBatchNo(item.getDeliveryBatch());
                feignService.updateOrderDeliveries(request);
            });
        }
        return Wrapper.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> applyAmount(ApplyAmountDO applyAmountDO, Long reconciliationId, UserLoginCacheDTO sysUser) {
        // step1: 查询对应对账单
        BusinessReconciliationDO entity = repository.findById(reconciliationId).orElse(null);
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        // step2: 校验请款单
        if(!entity.getStatus().equals(ReconciliationStatusEnum.APPLY.getCode())){
            return Wrapper.fail(ResponseCode.SA_BUSINESS_RECONCILIATION_CAN_NOT_APPLY);
        }
        // step3: 执行请款工作流
        TaskExecuteVO request = new TaskExecuteVO();
        request.setProcessKey(ProcessEnum.SETTLE_ACCOUNTS_BUSINESS_RECONCILIATION.getProcessKey());
        request.setAgree(-1);
        request.setMemberId(entity.getBuyerMemberId());
        request.setRoleId(entity.getBuyerRoleId());
        request.setDataId(entity.getId());
        request.setTaskId(entity.getTaskId());
        Wrapper<ComplexTaskCompleteVO> wrapper = feignService.completeBusinessReconciliation(request);
        // 提交流程实例失败回滚
        if (Optional.ofNullable(wrapper).map(w -> w.getCode()!=ResponseCode.SUCCESS.getCode()).orElse(true)) {
            // 回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(ResponseCode.SERVICE_WORKFLOW_ERROR);
        }
        ComplexTaskCompleteVO workStack = wrapper.getData();
        entity.setStatus(ReconciliationStatusEnum.APPLIED.getCode());
        entity.setOuterStatus(workStack.getOuterStatus());
        entity.setTaskId(workStack.getTaskId());
        // step4: 更新对账单
        repository.saveAndFlush(entity);
        // 添加对账单记录
        recordService.addRecord(sysUser,entity.getReconciliationNo(),entity.getStatus(), ReconciliationOperationEnum.SUB_APPLY.getMessage(),null);
        // step5: 开票
        MemberReceiptInvoiceBaseVO invoiceBaseVO = valueOfMemberReceiptInvoiceBaseVO(applyAmountDO, entity);
        invoiceService.save(invoiceBaseVO);

        return Wrapper.success();
    }


    private MemberReceiptInvoiceBaseVO valueOfMemberReceiptInvoiceBaseVO(ApplyAmountDO applyAmountDO, BusinessReconciliationDO entity) {
        // 查询明细
        List<BusinessReconciliationRowDO> rows = entity.getRows();
        BigDecimal taxRate = rows.stream().findFirst().map(BusinessReconciliationRowDO::getTaxRate).orElse(BigDecimal.ZERO);
        BigDecimal orderAmount = rows.stream().map(BusinessReconciliationRowDO::getCurrentMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        MemberReceiptInvoiceBaseVO saveVO = new MemberReceiptInvoiceBaseVO();
        // 会员id(供应商)
        saveVO.setMemberId(entity.getVendorMemberId());
        // 角色id(供应商)
        saveVO.setRoleId(entity.getVendorRoleId());
        // 支付会员id(采购商)
        saveVO.setPayMemberId(entity.getBuyerMemberId());
        // 支付角色id(采购商)
        saveVO.setPayRoleId(entity.getBuyerRoleId());
        // 单据编码: 请款单号
        saveVO.setOrderNo(applyAmountDO.getApplyNo());
        // 单据摘要
        saveVO.setOrderAbstract(entity.getReconciliationAbstract());
        // 单据类型 默认请款
        saveVO.setSettlementOrderType(SettlementOrderTypeEnum.RECONCILIATION.getCode());
        // 订单类型 默认其他
        saveVO.setOrderType(ReceiptInvoiceOrderTypeEnum.OTHER.getCode());
        // 单据日期
        saveVO.setOrderTime(entity.getCreateTime().toInstant(OffsetDateTime.now().getOffset()).toEpochMilli());
        // 单据明细金额
        saveVO.setOrderAmount(orderAmount.doubleValue());
        saveVO.setBatch(1);
        saveVO.setPayNode(SettlementHelpEnum.PAY_AFTER_SETTLEMENT.getMessage());
        // 支付金额
        saveVO.setPayAmount(orderAmount.doubleValue());
        // 支付时间
        // saveVO.setPayTime(0L);
        // 开票状态 默认未开票
        saveVO.setInvoiceStatus(ReceiptInvoiceStatusEnum.NO.getCode());
        // 支付状态
        saveVO.setPayStatus(ReceiptInvoicePayStatusEnum.NO.getCode());
        // 关联数据id : 请款单id
        saveVO.setDataId(applyAmountDO.getId());
        // 是否含税
        Integer isHasTax = taxRate.setScale(2, RoundingMode.HALF_UP).compareTo(BigDecimal.ZERO) > 0 ? CommonBooleanEnum.YES.getCode() : CommonBooleanEnum.NO.getCode();
        saveVO.setIsHasTax(isHasTax);
        // 税率
        saveVO.setTaxRate(taxRate.divide(BigDecimal.valueOf(100), 4,RoundingMode.HALF_UP).doubleValue());
        return saveVO;
    }

    /**
     * 对账单分页列表数据组装-供应商
     * @param result      分页对账单实体
     */
    private Wrapper<PageData<ToBeSubmitReconciliationVO>> buildPageCreatedReconciliation(Page<BusinessReconciliationDO> result){
        List<ToBeSubmitReconciliationVO> resultList = result.getContent().stream().map(r -> {
            ToBeSubmitReconciliationVO vo = new ToBeSubmitReconciliationVO();
            vo.setReconciliationId(r.getId());
            vo.setReconciliationNo(r.getReconciliationNo());
            vo.setReconciliationAbstract(r.getReconciliationAbstract());
            vo.setPayer(r.getPayer());
            vo.setReconciliationMoneyAmount(r.getReconciliationMoneyAmount());
            vo.setCreateTime(r.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setStatus(r.getStatus());
            vo.setStatusName(ReconciliationStatusEnum.getNameByCode(r.getStatus()));
            return vo;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(),resultList));
    }

    /**
     * 对账单分页列表数据组装-采购方
     * @param result      分页对账单实体
     */
    private Wrapper<PageData<ToBeConfirmReconciliationVO>> buildPageBuyerReconciliation(Page<BusinessReconciliationDO> result){
        List<ToBeConfirmReconciliationVO> resultList = result.getContent().stream().map(r -> {
            ToBeConfirmReconciliationVO vo = new ToBeConfirmReconciliationVO();
            vo.setReconciliationId(r.getId());
            vo.setReconciliationNo(r.getReconciliationNo());
            vo.setReconciliationAbstract(r.getReconciliationAbstract());
            vo.setPayee(r.getPayee());
            vo.setReconciliationMoneyAmount(r.getReconciliationMoneyAmount());
            vo.setCreateTime(r.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setStatus(r.getStatus());
            vo.setStatusName(ReconciliationStatusEnum.getNameByCode(r.getStatus()));
            return vo;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(),resultList));
    }

    /**
     * 待请款分页列表数据组装-采购方
     * @param result      分页对账单实体
     */
    private Wrapper<PageData<TobeApplyAmountListVO>> buildPageToBeApplyAmountReconciliation(Page<BusinessReconciliationDO> result){
        List<TobeApplyAmountListVO> resultList = result.getContent().stream().map(r -> {
            TobeApplyAmountListVO data = new TobeApplyAmountListVO();
            data.setInvoiceStatus(InvoiceStatusEnum.getNameByCode(r.getInvoiceStatus()));
            data.setReconciliationId(r.getId());
            data.setReconciliationNo(r.getReconciliationNo());
            data.setReconciliationAbstract(r.getReconciliationAbstract());
            data.setPayee(r.getPayee());
            data.setReconciliationMoneyAmount(r.getReconciliationMoneyAmount());
            data.setCreateTime(r.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            data.setStatus(r.getStatus());
            data.setStatusName(ReconciliationStatusEnum.getNameByCode(r.getStatus()));
            return data;
        }).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(result.getTotalElements(),resultList));
    }

}
